package de.dubiel.plung.server.services;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;

import com.google.appengine.api.channel.ChannelMessage;
import com.google.appengine.api.channel.ChannelService;
import com.google.appengine.api.channel.ChannelServiceFactory;
import com.google.appengine.api.users.UserServiceFactory;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;

import de.dubiel.plung.client.services.UserService;
import de.dubiel.plung.server.dao.DataStore;
import de.dubiel.plung.shared.constants.ChannelMessageConstants;
import de.dubiel.plung.shared.dao.HighScore;
import de.dubiel.plung.shared.dao.User;
import de.dubiel.plung.shared.exception.NoGoogleUserException;

/**
 * The server side implementation of the RPC service.
 */
@SuppressWarnings("serial")
public class UserServiceImpl extends RemoteServiceServlet implements UserService, HttpSessionListener {
    private static final String USERLIST_TOKENPREFIX = "userList";
    private final ChannelService channelService = ChannelServiceFactory.getChannelService();
    private final Map<String, User> loggedInUserList = new HashMap<String, User>();

    @Override
    public User getUser(String callbackURL) throws NoGoogleUserException {
        com.google.appengine.api.users.UserService userService = UserServiceFactory.getUserService();
        com.google.appengine.api.users.User appengineuser = userService.getCurrentUser();

        // Wenn kein Google-User angemeldet ist, auf die Anmeldeseite weiter
        // leiten
        if (appengineuser == null) {
            NoGoogleUserException e = new NoGoogleUserException();
            e.setLoginUrl(userService.createLoginURL(callbackURL));
            throw e;
        }
        String appengineuserID = appengineuser.getUserId();
        // Den Plung User in abhängigkeit von der Google-User ID aus der
        // Datenbank fischen
        User user = DataStore.findUserById(appengineuserID);
        if (user == null) {
            user = new User();
            user.setId(appengineuserID);
            user.setName(appengineuser.getNickname());
            DataStore.saveUser(user);
        }

        // Den Plung User in die Liste der eingeloggten User setzen
        loggedInUserList.put(appengineuserID, user);

        user.setUserListToken(channelService.createChannel(getChannelKey(user)));
        notifyUpdateUserLists();
        return user;
    }

    @Override
    public void updateUser(User user) {
        DataStore.saveUser(user);
    }

    public void getUserList() {

    }

    @Override
    public void sessionCreated(HttpSessionEvent arg0) {

    }

    @Override
    public void sessionDestroyed(HttpSessionEvent arg0) {
        com.google.appengine.api.users.UserService userService = UserServiceFactory.getUserService();
        com.google.appengine.api.users.User appengineuser = userService.getCurrentUser();
        if (appengineuser != null) {
            loggedInUserList.remove(appengineuser.getUserId());
            notifyUpdateUserLists();
        }
    }

    private void notifyUpdateUserLists() {
        for (User user : loggedInUserList.values()) {
            channelService.sendMessage(new ChannelMessage(getChannelKey(user), ChannelMessageConstants.UPDATEUSERLIST));
        }
    }

    private String getChannelKey(User user) {
        return USERLIST_TOKENPREFIX + user.getId();
    }

    @Override
    public HighScore getHighScoreList(int start, int length) {
        HighScore h = new HighScore();
        h.setHighScoreEntrys(DataStore.getNumRegisteredUsers());
        List<User> tmp = DataStore.getAllUsersSorted("-score");
        if (tmp.isEmpty())
            return h;
        h.setHighScoreList(new ArrayList<User>(tmp.subList(start, Math.min(start + length, tmp.size()))));
        return h;
    }
}
